//
//  TimerListener.swift
//  ClockWidget
//
//  Created by TSYH on 2023/9/12.
//

import SwiftUI

public protocol TimerListenerProtocol: AnyObject {
    func timerEventChangedHandler()
}

public class TimerListenCenter {
    public static let shared = TimerListenCenter()
    
    private lazy var listeners = [Int: NSHashTable<AnyObject>]()
    
    private var timeCounter = 0
    
    // 时间节点对应的响应者
    private var observers: [TimerListenerProtocol] {
        var result = [AnyObject]()
        for obj in listeners {
            if timeCounter%obj.key == 0 {
                result.append(contentsOf: obj.value.allObjects)
            }
        }
        return result as! [TimerListenerProtocol]
    }
    
    private var displayLinkTimer: CADisplayLink?

    func startTimer() {
        if displayLinkTimer == nil {
            displayLinkTimer = CADisplayLink(target: self, selector: #selector(timerHandler))
            displayLinkTimer?.preferredFramesPerSecond = 1
            displayLinkTimer?.add(to: .main, forMode: .default)
        }
    }
    
    @objc func timerHandler() {
        DispatchQueue.main.async {
            let observers = self.observers
            observers.forEach { listener in
                listener.timerEventChangedHandler()
            }
            self.timeCounter += 1
        }
    }
    
    private func endTimer() {
        timeCounter = 0
        if displayLinkTimer != nil {
            displayLinkTimer?.invalidate()
            displayLinkTimer = nil
        }
    }
    
    deinit {
        endTimer()
    }
    
    private func observers(with period: Int) -> NSHashTable<AnyObject> {
        if let obsers = listeners[period] {
            return obsers
        }
        return NSHashTable<AnyObject>(options: .weakMemory)
    }
    
    // MARK: public method
    public func add<T>(_ listener: T, period: Int = 1) where T: TimerListenerProtocol {
        #if DEBUG
//        return
        #endif
        
        let obsers = observers(with: period)
        obsers.add(listener)
        listeners[period] = obsers
        
        // 开启定时器
        startTimer()
    }
    
    public func remove<T>(_ listener: T) where T: TimerListenerProtocol {
        listeners.forEach({ $0.value.remove(listener) })
    }
    
    public func removeAll() {
        listeners.removeAll()
    }
}
